package com.kuizii.base.handler;


import com.kuizii.base.api.FeignResponseException;
import com.kuizii.base.exception.CustomException;
import com.kuizii.base.exception.DefaultExceptionResponse;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.net.SocketTimeoutException;
import java.sql.SQLException;
import java.util.*;

@Slf4j
@RestControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public DefaultExceptionResponse handlerException(Exception e) {

        MDC.put("errorCode", "500");
        log.error("Exception======>>>>" , e);
        DefaultExceptionResponse result = new DefaultExceptionResponse(e.getMessage(), HttpStatus.BAD_REQUEST.value(), e);
        return result;
    }


    @ExceptionHandler(CustomException.class)
    public ResponseEntity handlerCustomException(CustomException e) {
        DefaultExceptionResponse result = new DefaultExceptionResponse(e.getMessage(), e.getDetailMsg(), e.getHttpStatus(), e);
        result.setData(e.getData());
        result.setErrorCode(e.getErrorCode());
        MDC.put("errorCode", String.valueOf(e.getErrorCode()));
        log.error("CustomException======>>>>", e);
        return ResponseEntity.status(e.getHttpStatus()).body(result);
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler({ConstraintViolationException.class})
    public DefaultExceptionResponse handleConstraintViolationException(ConstraintViolationException e) {

        log.info("handleConstraintViolationException start, uri:{}, caused by: ", e);
        if (CollectionUtils.isEmpty(e.getConstraintViolations())) {
            return null;
        }

        Map<String, String> map = new HashMap<>();
        for (ConstraintViolation<?> cv : e.getConstraintViolations()) {

            String propertyPath = cv.getPropertyPath().toString();
            String filed = "";
            if (propertyPath.indexOf(".") != -1) {
                String[] propertyPathArr = propertyPath.split("\\.");
                filed = (propertyPathArr[propertyPathArr.length - 1]);
            } else {
                filed = (propertyPath);
            }
            map.put(filed, cv.getMessage());
        }

        DefaultExceptionResponse result = new DefaultExceptionResponse(map.toString());

        return result;
    }

    /**
     * 处理验证参数封装错误时异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler({HttpMessageNotReadableException.class})
    protected DefaultExceptionResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.info("handleConstraintViolationException start, uri:{}, caused by: ", e);

        DefaultExceptionResponse result = new DefaultExceptionResponse(e.getMessage());
        return result;
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(SQLException.class)
    public DefaultExceptionResponse handlerSQLException(SQLException e) {
        DefaultExceptionResponse result = new DefaultExceptionResponse("系统异常！");
        log.error("SQLException======>>>>" , e);
        return result;
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public DefaultExceptionResponse handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) {

        DefaultExceptionResponse result = new DefaultExceptionResponse("参数有误！");
        StringBuilder stringBuilder = new StringBuilder();
        List<String> errors = new ArrayList<>();

        Iterator<ObjectError> iterator = e.getBindingResult().getAllErrors().iterator();
        while (iterator.hasNext()) {
            ObjectError objectError = iterator.next();

            stringBuilder.append(objectError.getDefaultMessage());
            errors.add(objectError.getDefaultMessage());

        }
        result.setDetailMsg(stringBuilder.toString());
        result.setData(errors);
        log.error("MethodArgumentNotValidException======>>>>" + e, e);
        return result;
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BindException.class)
    public DefaultExceptionResponse handlerBindException(BindException e) {

        DefaultExceptionResponse result = new DefaultExceptionResponse("传入参数有误！");
        StringBuilder stringBuilder = new StringBuilder();
        Iterator<ObjectError> iterator = e.getBindingResult().getAllErrors().iterator();
        List<String> errors = new ArrayList<>();
        while (iterator.hasNext()) {
            ObjectError objectError = iterator.next();

            stringBuilder.append(objectError.getDefaultMessage());
            stringBuilder.append(System.lineSeparator());
            errors.add(objectError.getDefaultMessage());
        }
        result.setDetailMsg(stringBuilder.toString());
        result.setData(errors);
        log.error("BindException======>>>>" + e, e);
        return result;
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(SocketTimeoutException.class)
    public DefaultExceptionResponse handlerSocketTimeoutException(SocketTimeoutException e) {

        DefaultExceptionResponse result = new DefaultExceptionResponse("服务超时,刷新或稍后重试！");

        log.error("SocketTimeoutException======>>>>" , e);
        return result;
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(FeignResponseException.class)
    public DefaultExceptionResponse handlerFeignResponseException(FeignResponseException e) {

        DefaultExceptionResponse result = new DefaultExceptionResponse(e.getMessage(), e.getDetailMsg(), e.getStatusCode(), e.getCause());
        result.setData(e.getData());
        result.setErrorCode(e.getErrorCode());
        log.error("FeignResponseException======>>>>" + e, e);
        return result;
    }

}
